"""
Copyright Joshua Barron, Barron Software 2010.
hecate-profiler:

kanon.py
================================
Implements a "trusted server" k-anonymity gateway.
Currently, the gateway utilizes outside (experimental) information
to know the message set and other external variables.

Because this implementation acts as a local service to the profiler,
it is not concerned so much with the message as it is with the 
'mobile app' that is requesting location information.  In this
implementation the message commonality is based on the app id.
"""
import ast	#for converting from string to dict
from math import sqrt

class Message(object):
	"""
	Message class which represents a request for a location anonymization - includes actual location
	(simplified by x/y coords), time issued, k-value, app id, tolerances, etc.
	"""
	def __init__(self, app_id, time_issued, location, k, time_tolerance, loc_tolerance):
		self.app_id = app_id
		self.time = time_issued
		self.location = location		#tuple (x,y)
		self.k = k						#desired k-level of privacy
		self.tolerances = {"time":time_tolerance, "location":loc_tolerance}
		self.satisified = False

	def time_within_tolerance(self, time):
		#calculates whether or not the current time lapse is within temporal tolerances
		return time - self.time <= self.tolerances["time"]

	def distance_within_tolerance(self, location):
		#calculates the distance between actual location and given location and returns
		#whether or not the distance is within spatial tolerances
		distance = sqrt((location[0] - self.location[0])**2 + (location[1] - self.location[1])**2)
		return distance <= self.tolerances["location"]

	@staticmethod
	def create_from_string(input_string):
		try:
			d = ast.literal_eval(input_string)
			return Message(d['app_id'], d['time'], d['loc'], d['k'], d['ttol'], d['ltol']) 
		except:
			print "Could not convert input string [" + input_string + "] to message.\n"
			return None
		

class Anonymizer(object):

	def __init__(self, message_set=None):
		self.msg_set = {}			#currently available msgs, indexed by app_id (msg)
		self.future_msg_set = {}	#may become available in the future, indexed by time
		self.expired_msg_set = {}	#expired messages, indexed by app_id
		self.time_step = 0
		if message_set:
			self.setup_message_set(message_set)

	def increment_time_step(self):
		self.time_step += 1
		#check incoming messages
		if self.time_step in self.future_msg_set:
			#transfer all messages to current message_set
			for item in self.future_msg_set[self.time_step]:
				if item.app_id not in self.msg_set:
					self.msg_set[item.app_id] = [item]
				else:
					self.msg_set[item.app_id].append(item)
			del self.future_msg_set[self.time_step]
		#try to satisfy current requests
		self.satisfy_requests()

	def satisfy_requests(self):
		pass
				
	def setup_message_set(self, message_set):
		for item in message_set.items():
			if item.time <= self.time_step:	#message is 'old', we assume not expired though
				#index by app_id in current message set dict
				if item.app_id not in self.msg_set:
					self.msg_set[item.app_id] = [item]
				else:
					self.msg_set[item.app_id].append(item)
			elif item.time > self.time_step: #message is 'future', or incoming
				#index by time rather than app_id
				if item.time not in self.future_msg_set:
					self.future_msg_set[item.time] = [item]
				else:
					self.future_msg_set[item.time].append(item)
